iT邦幫忙

2023 iThome 鐵人賽

DAY 13
0

TypeScript 提供了多種內建的 Utility 型別,它是一組 內建的型別操作工具,可以幫助我們更輕鬆、更有效率地進行型別操作,今天威爾豬先介紹基本且常用的 Utility 型別:

Partial

前面威爾豬有稍微提到 可選串連 (?),而 Partial<T> 就是用於將物件型別的 所有屬性變為可選屬性,這對於在撰寫物件型別或函式參數時,允許部分屬性更新非常有用,可以減少重複的程式碼,更方便地操作物件。

用法:Partial<型別名稱>

例如:

interface IPerson {
  name: string;
  age: number;
  address: string;
}

// ⭕️ 這樣即使沒有撰寫 address 也不會噴錯
const person1: Partial<IPerson> = {
  name: "威爾豬",
  age: 3,
};

// ❌
const person2: IPerson = {
  name: "威爾豬",
  age: 3,
};

https://ithelp.ithome.com.tw/upload/images/20230912/20141250v9WwRzWx6b.png

我們再看另一個範例:

假設我們一樣有一個 IPerson 接口,它包含了名字、年齡和地址屬性,我們想要編寫一個函數,允許我們更新 IPerson 部分屬性的值:

interface IPerson {
  name: string;
  age: number;
  address: string;
}

type TPartialPerson = Partial<IPerson>;

const updatePersonFn = (
  person: IPerson,
  updatePersonData: TPartialPerson
): IPerson => {
  return { ...person, ...updatePersonData };
};

const person: IPerson = {
  name: "威爾豬",
  age: 3,
  address: "皇后大道 123 街",
};

const updatePerson = updatePersonFn(person, { age: 4 });

console.log(updatePerson); // 輸出: { name: '威爾豬', age: 4, address: '皇后大道 123 街' }

在上面的範例中,我們首先聲明了 IPerson 接口。然後,我們創建了一個 updatePersonFn 函式,它接受一個接口為 IPerson 的 person 物件和一個類型別名為 TPartialPerson 的 updatePersonData 物件 (即 IPerson 的部分屬性),函式內使用展開運算符 (...) 將 updatePersonData 物件合併到原始 person 物件中,這樣就實現了部分屬性的更新。

除了創建新的類型別名外,我們也可以使用 接口繼承 來操作 Partial,看以下範例:

interface IPerson {
  name: string;
  age: number;
  job: string;
}

// ⭕️ 使用接口繼承將所有屬性變成是可選的
interface IPartialPerson extends Partial<IPerson> {}

// ⭕️ 創建新的類型別名將所有屬性變成是可選的
type TPartialPerson = Partial<IPerson>; 

const person1: IPartialPerson = { name: "威爾豬", job: "首富" };
const person2: TPartialPerson = { name: "威爾豬", age: 3 };
const person3: Partial<IPerson> = { name: "威爾豬" }; // ⭕️ 當然也可以直接這樣寫

上面範例的寫法都可以,就看我們個人或專案結構要怎麼使用比較方便了。

Required

而和 Partial 相反的就是 Required<T>,它是將物件型別中的 所有屬性變成必需屬性。這個威爾豬比較少會用到,但還是介紹一下有這用法。

用法:Required<型別名稱>

看以下範例:

interface IPerson {
  name: string;
  age?: number;
  address?: string;
}

type RequiredPerson = Required<IPerson>;

// ⭕️
const requiredPerson1: IPerson = {
  name: "威爾豬",
  address: "皇后大道 123 街",
};

// ❌ 全部屬性都需撰寫
const requiredPerson2: RequiredPerson = {
  name: "威爾豬",
};

https://ithelp.ithome.com.tw/upload/images/20230913/20141250r5JL5RZ4yz.png

Pick

Pick<T, K> 可從一個型別 T 中選取特定屬性 K,也就是允許我們從一個物件型別中 選擇指定的屬性,並創建一個新的類型別名。

用法:Pick<型別名稱, "選擇的屬性 1" | "選擇的屬性 2" | ......>

例如,我們還是一樣有一個 IPerson 的接口,我們想要創建一個新的類型別名,只包含 name 和 address 這兩個屬性,就可以使用 Pick 來實現:

interface IPerson {
  name: string;
  age: number;
  address: string;
};

// 使用 Pick 創建選擇 name、address 屬性的 TPickPerson 類型別名
type TPickPerson = Pick<IPerson, "name" | "address">;

const pickPerson: TPickPerson = {
  name: "威爾豬",
  address: "皇后大道 123 街",
};

Omit

與 Pick 相反的就是 Omit<T, K> 可從一個型別 T 中排除特定屬性 K,也就是允許我們從一個物件型別中 排除指定的屬性,並創建一個新的類型別名。

用法:Omit<型別名稱, "排除的屬性 1" | "排除的屬性 2" | ......>

如果我們想要依據 IPerson 接口來創建一個新的類型別名,並排除 age 屬性和 address 屬性,我們可以使用 Omit

interface IPerson {
  name: string;
  age: number;
  address: string;
};

// 使用 Omit 創建排除 age、address 屬性的 TOmitPerson 類型別名
type TOmitPerson = Omit<IPerson, "age" | "address">;

const omitPerson: TOmitPerson = {
  name: "威爾豬",
};

Utility 型別有非常多種,有興趣的小夥伴可以自行到官網查看 Utility 型別。以上只是小小的一部分,但威爾豬自己還滿常使用的,之後的章節威爾豬會再介紹 其它的 Utility 型別

總結來說,使用 Partial、Required、Pick 和 Omit 等基本的 Utility 型別,可以 避免型別被重複定義創建更精確的類型別名 以及在不同的情境下可以 更靈活地運用型別,就看我們運用哪一種方式來簡化程式碼,讓程式碼更具可讀性。


上一篇
接口 / 介面 (Interface)
下一篇
類型別名 VS. 接口 (type VS. interface)
系列文
用不到 30 天學會基本 TypeScript30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言